﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Security;

namespace WindowsFormsApplication1
{
    public class GameTime
    {
        // Fields
        private TimeSpan elapsedGameTime;
        private TimeSpan elapsedRealTime;
        private bool isRunningSlowly;
        private TimeSpan totalGameTime;
        private TimeSpan totalRealTime;

        // Methods
        public GameTime()
        {
        }

        public GameTime(TimeSpan totalRealTime, TimeSpan elapsedRealTime, TimeSpan totalGameTime, TimeSpan elapsedGameTime)
            : this(totalRealTime, elapsedRealTime, totalGameTime, elapsedGameTime, false)
        {
        }

        public GameTime(TimeSpan totalRealTime, TimeSpan elapsedRealTime, TimeSpan totalGameTime, TimeSpan elapsedGameTime, bool isRunningSlowly)
        {
            this.totalRealTime = totalRealTime;
            this.elapsedRealTime = elapsedRealTime;
            this.totalGameTime = totalGameTime;
            this.elapsedGameTime = elapsedGameTime;
            this.isRunningSlowly = isRunningSlowly;
        }

        // Properties
        public TimeSpan ElapsedGameTime
        {
            get
            {
                return this.elapsedGameTime;
            }
            internal set
            {
                this.elapsedGameTime = value;
            }
        }

        // ElapsedRealTime表示相邻两帧之间的时间间隔
        public TimeSpan ElapsedRealTime
        {
            get
            {
                return this.elapsedRealTime;
            }
            internal set
            {
                this.elapsedRealTime = value;
            }
        }

        public bool IsRunningSlowly
        {
            get
            {
                return this.isRunningSlowly;
            }
            internal set
            {
                this.isRunningSlowly = value;
            }
        }

        public TimeSpan TotalGameTime
        {
            get
            {
                return this.totalGameTime;
            }
            internal set
            {
                this.totalGameTime = value;
            }
        }

        public TimeSpan TotalRealTime
        {
            get
            {
                return this.totalRealTime;
            }
            internal set
            {
                this.totalRealTime = value;
            }
        }
    }

    class GameClock
    {
        // Fields
        private long baseRealTime;
        private TimeSpan currentTimeBase;
        private TimeSpan currentTimeOffset;
        private TimeSpan elapsedAdjustedTime;
        private TimeSpan elapsedTime;
        private long lastRealTime;
        private bool lastRealTimeValid;
        private int suspendCount;
        private long suspendStartTime;
        private long timeLostToSuspension;

        // Methods
        public GameClock()
        {
            this.Reset();
        }

        private static TimeSpan CounterToTimeSpan(long delta)
        {
            long num = 0x989680L;
            long num2 = (delta * num) / Frequency;
            return TimeSpan.FromTicks(num2);
        }

        internal void Reset()
        {
            this.currentTimeBase = TimeSpan.Zero;
            this.currentTimeOffset = TimeSpan.Zero;
            this.baseRealTime = Counter;
            this.lastRealTimeValid = false;
        }

        internal void Resume()
        {
            this.suspendCount--;
            if (this.suspendCount <= 0)
            {
                long counter = Counter;
                this.timeLostToSuspension += counter - this.suspendStartTime;
                this.suspendStartTime = 0L;
            }
        }

        internal void Step()
        {
            long counter = Counter;
            if (!this.lastRealTimeValid)
            {
                this.lastRealTime = counter;
                this.lastRealTimeValid = true;
            }
            try
            {
                this.currentTimeOffset = CounterToTimeSpan(counter - this.baseRealTime);
            }
            catch (OverflowException)
            {
                this.currentTimeBase += this.currentTimeOffset;
                this.baseRealTime = this.lastRealTime;
                try
                {
                    this.currentTimeOffset = CounterToTimeSpan(counter - this.baseRealTime);
                }
                catch (OverflowException)
                {
                    this.baseRealTime = counter;
                    this.currentTimeOffset = TimeSpan.Zero;
                }
            }
            try
            {
                this.elapsedTime = CounterToTimeSpan(counter - this.lastRealTime);
            }
            catch (OverflowException)
            {
                this.elapsedTime = TimeSpan.Zero;
            }
            try
            {
                long num2 = this.lastRealTime + this.timeLostToSuspension;
                this.elapsedAdjustedTime = CounterToTimeSpan(counter - num2);
                this.timeLostToSuspension = 0L;
            }
            catch (OverflowException)
            {
                this.elapsedAdjustedTime = TimeSpan.Zero;
            }
            this.lastRealTime = counter;
        }

        internal void Suspend()
        {
            this.suspendCount++;
            if (this.suspendCount == 1)
            {
                this.suspendStartTime = Counter;
            }
        }

        // Properties
        internal static long Counter
        {
            get
            {
                return Stopwatch.GetTimestamp();
            }
        }

        internal TimeSpan CurrentTime
        {
            get
            {
                return (this.currentTimeBase + this.currentTimeOffset);
            }
        }

        internal TimeSpan ElapsedAdjustedTime
        {
            get
            {
                return this.elapsedAdjustedTime;
            }
        }

        internal TimeSpan ElapsedTime
        {
            get
            {
                return this.elapsedTime;
            }
        }

        internal static long Frequency
        {
            get
            {
                return Stopwatch.Frequency;
            }
        }
    }


    public class Game : Form
    {
        // Fields
        private TimeSpan accumulatedElapsedGameTime = TimeSpan.Zero;
        private GameClock clock = null;
        private bool drawRunningSlowly = false;
        private bool exitRequested = false;
        private bool isActive = false;
        private bool forceElapsedTimeToZero = true;
        private TimeSpan _elapsedTime = TimeSpan.Zero;
        private GameTime gameTime = null;
        private TimeSpan inactiveSleepTime = TimeSpan.FromMilliseconds(20.0);
        private bool isFixedTimeStep = true;
        private TimeSpan lastFrameElapsedGameTime = TimeSpan.Zero;
        private TimeSpan lastFrameElapsedRealTime;
        private readonly TimeSpan maximumElapsedTime = TimeSpan.FromMilliseconds(500.0);
        private bool suppressDraw = false;
        private TimeSpan targetElapsedTime = TimeSpan.FromTicks(0x28b0bL);
        private TimeSpan totalGameTime = TimeSpan.Zero;
        private int updatesSinceRunningSlowly1 = 0x7fffffff;
        private int updatesSinceRunningSlowly2 = 0x7fffffff;

        // Methods
        public Game()
        {
            this.clock = new GameClock();
            this.gameTime = new GameTime();
            this.gameTime.ElapsedGameTime = TimeSpan.Zero;
            // 其中gameTime.ElapsedRealTime表示相邻两帧之间的时间间隔
            this.gameTime.ElapsedRealTime = TimeSpan.Zero;
            this.gameTime.TotalGameTime = this.totalGameTime;
            this.gameTime.TotalRealTime = this.clock.CurrentTime;
            this.gameTime.IsRunningSlowly = false;
            this.Activated += new EventHandler(Game_Activated);
            this.Deactivate += new EventHandler(Game_Deactivate);
        }

        void Game_Deactivate(object sender, EventArgs e)
        {
            isActive = false;
        }

        void Game_Activated(object sender, EventArgs e)
        {
            isActive = true;
        }

        public void Resume()
        {
            this.clock.Resume();
        }

        public void Suspend()
        {
            this.clock.Suspend();
        }

        public void ResetElapsedTime()
        {
            this.forceElapsedTimeToZero = true;
            this.drawRunningSlowly = false;
            this.updatesSinceRunningSlowly1 = 0x7fffffff;
            this.updatesSinceRunningSlowly2 = 0x7fffffff;
        }



        [StructLayout(LayoutKind.Sequential)]
        private struct Message
        {
            public IntPtr hWnd;
            public uint uMsg;
            public IntPtr wParam;
            public IntPtr lParam;
            public uint uTime;
            public Point mousePos;
        }

        [return: MarshalAs(UnmanagedType.Bool)]
        [SuppressUnmanagedCodeSecurity, DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern bool PeekMessage(
            out Message msg, IntPtr hWnd, uint uMsgMin, uint uMsgMax, uint flags);

        public void OnIdle()
        {
            Message message;
            while (!PeekMessage(out message, IntPtr.Zero, 0, 0, 0))
            {
                if (this.exitRequested)
                {
                    this.Close();
                }
                else
                {
                    this.Tick();
                    Thread.Sleep(1);
                }
            }
        }

        public void SuppressDraw()
        {
            this.suppressDraw = true;
        }

        private void Tick()
        {
            if (!this.ShouldExit)
            {
                if (!this.isActive)
                {
                    Thread.Sleep((int)this.inactiveSleepTime.TotalMilliseconds);
                }
                this.clock.Step();
                bool flag = true;
                this.gameTime.TotalRealTime = this.clock.CurrentTime;
                // 其中gameTime.ElapsedRealTime表示相邻两帧之间的时间间隔
                this.gameTime.ElapsedRealTime = this.clock.ElapsedTime;
                this.lastFrameElapsedRealTime += this.clock.ElapsedTime;
                TimeSpan elapsedAdjustedTime = this.clock.ElapsedAdjustedTime;
                if (elapsedAdjustedTime < TimeSpan.Zero)
                {
                    elapsedAdjustedTime = TimeSpan.Zero;
                }
                if (this.forceElapsedTimeToZero)
                {
                    // 其中gameTime.ElapsedRealTime表示相邻两帧之间的时间间隔
                    this.gameTime.ElapsedRealTime = this.lastFrameElapsedRealTime = elapsedAdjustedTime = TimeSpan.Zero;
                    elapsedAdjustedTime = TimeSpan.Zero;
                    this.forceElapsedTimeToZero = false;
                }
                if (elapsedAdjustedTime > this.maximumElapsedTime)
                {
                    elapsedAdjustedTime = this.maximumElapsedTime;
                }
                if (this.isFixedTimeStep)
                {
                    if (Math.Abs((long)(elapsedAdjustedTime.Ticks - this.targetElapsedTime.Ticks)) < (this.targetElapsedTime.Ticks >> 6))
                    {
                        elapsedAdjustedTime = this.targetElapsedTime;
                    }
                    this.accumulatedElapsedGameTime += elapsedAdjustedTime;
                    long num = this.accumulatedElapsedGameTime.Ticks / this.targetElapsedTime.Ticks;
                    this.accumulatedElapsedGameTime = TimeSpan.FromTicks(this.accumulatedElapsedGameTime.Ticks % this.targetElapsedTime.Ticks);
                    this.lastFrameElapsedGameTime = TimeSpan.Zero;
                    if (num == 0L)
                    {
                        return;
                    }
                    TimeSpan targetElapsedTime = this.targetElapsedTime;
                    if (num > 1L)
                    {
                        this.updatesSinceRunningSlowly2 = this.updatesSinceRunningSlowly1;
                        this.updatesSinceRunningSlowly1 = 0;
                    }
                    else
                    {
                        if (this.updatesSinceRunningSlowly1 < 0x7fffffff)
                        {
                            this.updatesSinceRunningSlowly1++;
                        }
                        if (this.updatesSinceRunningSlowly2 < 0x7fffffff)
                        {
                            this.updatesSinceRunningSlowly2++;
                        }
                    }
                    this.drawRunningSlowly = this.updatesSinceRunningSlowly2 < 20;
                    while ((num > 0L) && !this.ShouldExit)
                    {
                        num -= 1L;
                        try
                        {
                            this.gameTime.ElapsedGameTime = targetElapsedTime;
                            this.gameTime.TotalGameTime = this.totalGameTime;
                            this.gameTime.IsRunningSlowly = this.drawRunningSlowly;

                            _elapsedTime += gameTime.ElapsedGameTime;
                            if (_elapsedTime > this.targetElapsedTime)
                            {
                                _elapsedTime -= this.targetElapsedTime;
                                this.Update(this.gameTime);
                            }

                            flag &= this.suppressDraw;
                            //this.suppressDraw = false;
                        }
                        finally
                        {
                            this.lastFrameElapsedGameTime += targetElapsedTime;
                            this.totalGameTime += targetElapsedTime;
                        }
                    }
                }
                else
                {
                    TimeSpan span3 = elapsedAdjustedTime;
                    this.drawRunningSlowly = false;
                    this.updatesSinceRunningSlowly1 = 0x7fffffff;
                    this.updatesSinceRunningSlowly2 = 0x7fffffff;
                    if (!this.ShouldExit)
                    {
                        try
                        {
                            this.gameTime.ElapsedGameTime = this.lastFrameElapsedGameTime = span3;
                            this.gameTime.TotalGameTime = this.totalGameTime;
                            this.gameTime.IsRunningSlowly = false;

                            _elapsedTime += gameTime.ElapsedGameTime;
                            if (_elapsedTime > this.targetElapsedTime)
                            {
                                _elapsedTime -= this.targetElapsedTime;
                                this.Update(this.gameTime);
                            }

                            flag &= this.suppressDraw;
                            //this.suppressDraw = false;
                        }
                        finally
                        {
                            this.totalGameTime += span3;
                        }
                    }
                }
                if (!flag)
                {
                    this.DrawFrame();
                }
            }
        }

        private void DrawFrame()
        {
            try
            {
                if (!this.ShouldExit  && !this.IsMinimized)
                {
                    this.gameTime.TotalRealTime = this.clock.CurrentTime;
                    // 其中gameTime.ElapsedRealTime表示相邻两帧之间的时间间隔
                    this.gameTime.ElapsedRealTime = this.lastFrameElapsedRealTime;
                    this.gameTime.TotalGameTime = this.totalGameTime;
                    this.gameTime.ElapsedGameTime = this.lastFrameElapsedGameTime;
                    this.gameTime.IsRunningSlowly = this.drawRunningSlowly;
                    this.Draw(this.gameTime);
                }
            }
            finally
            {
                this.lastFrameElapsedRealTime = TimeSpan.Zero;
                this.lastFrameElapsedGameTime = TimeSpan.Zero;
            }
        }

        protected virtual void Draw(GameTime gameTime)
        {
            // 当运行程序时，XNA会以尽可能大的频率调用Draw方法，只由下面两个规则限制：

            //1, 没有必要将调用Draw的频率大于屏幕刷新频率。
            //   如果屏幕刷新频率只有每秒100次，每秒绘制110次是无用的。
            //   屏幕刷新率是由PC屏幕和它的设置决定的，Zune 30每秒刷新60次，其他Zune设备每秒刷新30次。 
            //2, Update方法每秒调用60次。
            //   如果游戏计算量太大，Draw方法调用次数会变少以保证Update方法可以每秒调用60次。 

        }

        protected virtual void Update(GameTime gameTime)
        {
            // 在每一帧被绘制之前，Update方法将被调用
            // 它将更新你的游戏时间、输入、声音和其他一切在荧屏上不可见的事物。 
            // 即使你的游戏被GPU限制，Update方法也能很好的运行。

            // 你可以指定Update的更新频率，但如果频率太高，XNA将无法达到
            // 在这种情况下，gameTime.IsRunningSlowly 变量会被设置为true

            // Update和Draw方法调用频率的重要性:
            // 因为你将更新逻辑放置在Update方法中，Update频率的减少会导致
            // 游戏中的所有物体变慢，这非常令人讨厌。
            // 当Draw调用频率小于屏幕刷新频率时，只有游戏的视觉表现会暂时受影响，
            // 游戏帧频率暂时由每秒100帧降到80帧不容易察觉。
            // 因此，如前所述，如果必要，XNA会降低Draw的频率
            // 以保证Update能以每秒60帧的频率调用。 

            // 一般需要计算每一帧要移动多少像素
            // 如果一秒钟只画一帧的前提下，移动400像素
            // 那么如果一秒钟画60帧，那么一帧需要1/60秒，此时像素移动就是400/60
            // gameTime.ElapsedRealTime表示相邻两帧之间的时间间隔
        }

        internal bool IsMinimized
        {
            get
            {
                if (base.ClientSize.Width != 0)
                {
                    return (base.ClientSize.Height == 0);
                }
                return true;
            }
        }

        public TimeSpan InactiveSleepTime
        {
            get
            {
                return this.inactiveSleepTime;
            }
            set
            {
                if (value < TimeSpan.Zero)
                {
                    this.inactiveSleepTime = TimeSpan.FromMilliseconds(20.0);
                }
                else
                {
                    this.inactiveSleepTime = value;
                }
            }
        }

        //默认情况下Update方法每秒调用60次，或者说每隔16.667毫秒更新一次。
        //可以通过改变TargetElapsedTime变量改变更新频率
        //      this.TargetElapsedTime = TimeSpan.FromSeconds(1.0f / 1500.0f);
        //当调用到这行代码时，XNA会以每秒1500次的频率调用Update方法。

        //可以让XNA不按固定时间间隔调用Update方法
        //要做到这点，需要将IsFixedTimeStep变量设置为false。
        public bool IsFixedTimeStep
        {
            get
            {
                return this.isFixedTimeStep;
            }
            set
            {
                this.isFixedTimeStep = value;
            }
        }

        public TimeSpan TargetElapsedTime
        {
            get
            {
                return this.targetElapsedTime;
            }
            set
            {
                if (value <= TimeSpan.Zero)
                {
                    this.targetElapsedTime = TimeSpan.FromTicks(0x28b0bL);
                }
                else
                {
                    this.targetElapsedTime = value;
                }
            }
        }

        private bool ShouldExit
        {
            get
            {
                return this.exitRequested;
            }
        }



        /// <summary>
        /// FPS（Frames Per Second）：每秒传输帧数。 
        /// 动画播放速度的单位是fps
        /// 其中的f就是英文单词Frame(画面、帧)，p就是Per(每)，s就是Second(秒)。
        /// 用中文表达就是多少帧每秒，或每秒多少帧。
        /// </summary>
        public int FPS
        {
            get
            {
                if (this.targetElapsedTime.Milliseconds > 0)
                {
                    return 1000 / this.targetElapsedTime.Milliseconds;
                }
                else
                {
                    this.targetElapsedTime = TimeSpan.FromTicks(0x28b0bL);
                }
                return 62;
            }
            set
            {
                if (value > 0)
                {
                    TargetElapsedTime = TimeSpan.FromSeconds(1.0f / value);
                }
                else
                {
                    this.targetElapsedTime = TimeSpan.FromTicks(0x28b0bL);
                }
            }
        }

    }


}
